﻿/*
	Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "Scale9Image.h"
#include "../node/SpriteFrame.h"
#include "../base/Debuger.h"
#include "../render/ProgramCache.h"
#include "../render/ProgramStateCache.h"
#include "../render/Texture2D.h"
#include "../render/Material.h"
#include "../render/Scale9ImageCommand.h"
#include "../render/Camera.h"

#include <functional>

namespace SkyEngine2d
{

	Scale9Image* Scale9Image::create(const std::wstring& file_name, const Rect cap_insets)
	{
		auto p = new Scale9Image();
		if (p && p->init(file_name, cap_insets))
		{
			return p;
		}
		return nullptr;
	}

	std::unique_ptr<Scale9Image> Scale9Image::createUniquePtr(const std::wstring& file_name, const Rect cap_insets)
	{
		auto p = std::unique_ptr< Scale9Image>(new Scale9Image());
		if (p && p->init(file_name, cap_insets))
		{
			return p;
		}
		return nullptr;
	}

	std::unique_ptr<Scale9Image> Scale9Image::createUniquePtr(const SpriteFrameSharedPtr& sprite_frame, const Rect& cap_insets)
	{
		auto p = std::unique_ptr< Scale9Image>(new Scale9Image());
		if (p && p->init(sprite_frame, cap_insets))
		{
			return p;
		}
		return nullptr;
	}

	Scale9Image* Scale9Image::create(const SpriteFrameSharedPtr& sprite_frame, const Rect& cap_insets)
	{
		auto p = new Scale9Image();
		if (p && p->init(sprite_frame, cap_insets))
		{
			return p;
		}
		return nullptr;
	}

	void Scale9Image::draw(IRender* render, const Matrix4X4& transform)
	{
		if (m_isUpdateVertexDate || (m_mat != transform))
		{
			for (int i = 0; i < m_vertexBuffer.size(); i++)
			{
				transform.transformVector(m_vertexBuffer[i].position, &m_tempVertexBuffer[i].position);
			}
			m_mat = transform;
			m_isUpdateVertexDate = false;
		}

		m_cmd->update(m_material, m_tempVertexBuffer);
		render->addRenderCommand(m_cmd.get());
	}

	void Scale9Image::draw(IRender* render, const Rect& draw_rect, Camera* cramer)
	{
		if (!(m_lastDrawRect.getSize() == draw_rect.getSize()))
		{
			this->setDisplaySize(draw_rect.getSize());
		}

		if (m_lastDrawRect.getBottomLeft() != draw_rect.getBottomLeft())
		{
			auto mat = DirectX::XMMatrixMultiply(DirectX::XMMatrixTranslation(draw_rect.getBottomLeft().x, draw_rect.getBottomLeft().y, 0.0f), cramer->getViewProjectMat());
			this->draw(render, mat);
		}
	}

	void Scale9Image::setScalingX(float sx)
	{
		auto per_w = m_imageRect.getSize().width;
		auto now = per_w * sx;
		this->updateVertexHorizontal(now);
		m_displaySize.width = now;
	}


	void Scale9Image::setScalingY(float yx)
	{
		auto now = yx * m_imageRect.getSize().height;
		this->updateVertexVertical(now);
		m_displaySize.height = now;
	}

	void Scale9Image::setDisplaySize(const Size& size)
	{
		if (m_displaySize == size)
		{
			return;
		}

		this->updateVertexHorizontal(size.width);
		this->updateVertexVertical(size.height);

		m_displaySize = size;

		return;
	}

	const Size& Scale9Image::getDisplaySize() const
	{
		return m_displaySize;
	}

	const Size& Scale9Image::getImageSize() const
	{
		return m_imageRect.getSize();
	}

	Scale9Image::Scale9Image()
		:m_isUpdateVertexDate(true)
	{

	}



	Scale9Image::~Scale9Image()
	{

	}

	bool Scale9Image::init(const std::wstring& file_name, const Rect cap_insets)
	{
		if (file_name.size() <= 0)
		{
			return false;
		}

		auto sf = SpriteFrame::createFromPicture(file_name);
		if (!sf)
		{
			return false;
		}
		return this->init(sf, cap_insets);
	}

	bool Scale9Image::init(const SpriteFrameSharedPtr& sprite_frame, const Rect cap_insets)
	{
		m_spriteFrame = sprite_frame;
		m_imageRect = m_spriteFrame->getRect();
		auto texture = m_spriteFrame->getTexture().lock();
		if (!texture)
		{
			return false;
		}
		m_textureSize = texture->getContentSize();

		auto s = ProgramStateCache::getInstance();
		m_material = std::make_shared<Material>(
			m_spriteFrame->getTexture().lock(),
			s->getBlendState()->NonPremultiplied(),
			s->getSamplerState()->LinearClamp(),
			s->getRasterizerState()->CullCounterClockwise(),
			s->getDepthStencilState()->DepthNone(),
			ProgramCache::getInstance()->getProgramByName(ProgramCache::SHADER_POSITION_COLOR_TEXTURE)
			);
		this->setCapInsets(cap_insets);
		m_cmd = std::make_unique<Scale9ImageCommand>();
		this->update();
		m_cmd->update(m_material, m_vertexBuffer);

		return true;
	}

	void Scale9Image::setCapInsets(const Rect& cap_insets)
	{
		m_capInsets = cap_insets;
		//如果没有指定中间区域的大小 ，则将图片进行3等分处理
		if (m_capInsets == Rect::Zero)
		{
			auto w = m_imageRect.getSize().width / 3;
			auto h = m_imageRect.getSize().height / 3;

			m_capInsets = Rect(w, h, w, h);
		}
	}

	void Scale9Image::update()
	{
		//图片的数据信息
		const float image_left = 0;
		const float image_right = m_imageRect.getWidth();
		const float image_bottom = 0;
		const float image_top = m_imageRect.getHeight();

		//点9图的数据信息
		const float left = m_capInsets.getLeft();
		const float right = m_capInsets.getRight();
		const float bottom = m_capInsets.getBottom();
		const float top = m_capInsets.getTop();

		//纹理的大小数据
		const float texWidth = m_textureSize.width;
		const float texHeight = m_textureSize.height;

		//图片在纹理中的位置
		float in_texture_x = m_imageRect.getLeft();
		float in_texture_y = m_imageRect.getBottom();

		//生成顶点信息
		auto func = [&](float x, float y) {//
			Vector3 vpos(x, y, 0.0f);  //顶点坐标
			Vector2 texPos((x + in_texture_x) / texWidth, 1 - (y + in_texture_y) / texHeight); //纹理坐标
			m_originalVertexBuffer.push_back(VertexPositionColorTexture(vpos,Color4f::White,texPos ));
		};

		//顶点0
		func(image_left, image_top);
		//顶点1
		func(left, image_top);
		//顶点2
		func(right, image_top);
		//顶点3
		func(image_right, image_top);

		//顶点4
		func(image_left, top);
		//顶点5
		func(left, top);
		//顶点6
		func(right, top);
		//顶点7
		func(image_right, top);

		//顶点8
		func(image_left, bottom);
		//顶点9
		func(left, bottom);
		//顶点10
		func(right, bottom);
		//顶点11
		func(image_right, bottom);

		//顶点12
		func(image_left, image_bottom);
		//顶点13
		func(left, image_bottom);
		//顶点14
		func(right, image_bottom);
		//顶点15
		func(image_right, image_bottom);

		m_tempVertexBuffer = m_vertexBuffer = m_originalVertexBuffer;
	}


	void Scale9Image::updateVertexHorizontal(float now_width)
	{
		static int arr_index_add[]{ 2,6,10,14,3,7,11,15 };
		auto per_w = m_imageRect.getSize().width;
		float half_dt = (now_width - per_w);

		for (auto& i : arr_index_add)
		{
			m_vertexBuffer[i].position.x = m_originalVertexBuffer[i].position.x + half_dt;
		}

		m_isUpdateVertexDate = true;
	}

	void Scale9Image::updateVertexVertical(float now_height)
	{
		auto per_h = m_imageRect.getSize().height;
		float half_dt = (now_height - per_h);

		for (int i = 0; i < 8; i++)
		{
			m_vertexBuffer[i].position.y = m_originalVertexBuffer[i].position.y + half_dt;
		}

		m_isUpdateVertexDate = true;
	}
}
